From: Jeroen van der Heijden Date: Fri, 5 Oct 2018 20:44:24 +0000 (+0200) Subject: Finished integration tests X-Git-Tag: archive/raspbian/2.0.44-1+rpi1~1^2~3^2~8^2~32 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=fef9022098f435db589b961b2473ed873e03541b;p=siridb-server.git Finished integration tests --- diff --git a/itest/.dockerignore b/itest/.dockerignore new file mode 100644 index 00000000..e5482ab8 --- /dev/null +++ b/itest/.dockerignore @@ -0,0 +1,4 @@ +testdir/ +Dockerfile +*.log +__pycache__/ diff --git a/itest/Dockerfile b/itest/Dockerfile index 21b0473d..d202d613 100644 --- a/itest/Dockerfile +++ b/itest/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:18.04 +FROM ubuntu:18.04 as builder COPY ./main.c ./main.c COPY ./src/ ./src/ COPY ./include/ ./include/ @@ -16,9 +16,15 @@ RUN cd ./Release && \ FROM python RUN apt-get update && \ - apt-get install -y valgrind -COPY --from=0 ./Release/siridb-server /Release/siridb-server + apt-get install -y \ + valgrind \ + libuv1 \ + libpcre2-8-0 && \ + wget https://github.com/SiriDB/siridb-admin/releases/download/1.1.3/siridb-admin_1.1.3_linux_amd64.bin -O /usr/local/bin/siridb-admin && \ + chmod +x /usr/local/bin/siridb-admin +COPY --from=builder ./Release/siridb-server /Release/siridb-server +COPY --from=builder /usr/lib/x86_64-linux-gnu/libcleri* /usr/lib/x86_64-linux-gnu/ COPY ./itest/ /itest/ WORKDIR /itest RUN pip install -r requirements.txt -CMD [ "python", "test_select.py" ] \ No newline at end of file +CMD [ "python", "run_all.py", "-m", "-b=Release" ] diff --git a/itest/run_all.py b/itest/run_all.py index 9ea0bfa7..344c1d9b 100644 --- a/itest/run_all.py +++ b/itest/run_all.py @@ -1,6 +1,7 @@ #!/usr/bin/python3 from testing import run_test from testing import Server +from testing import parse_args from test_cluster import TestCluster from test_group import TestGroup from test_list import TestList @@ -17,10 +18,8 @@ from test_pipe_support import TestPipeSupport from test_buffer import TestBuffer -Server.BUILDTYPE = 'Release' - if __name__ == '__main__': - # run_test(TestCluster()) + parse_args() run_test(TestCompression()) run_test(TestGroup()) run_test(TestList()) diff --git a/itest/test_group.py b/itest/test_group.py index 4a536870..3e07edf8 100644 --- a/itest/test_group.py +++ b/itest/test_group.py @@ -36,17 +36,17 @@ class TestGroup(TestBase): async def run(self): await self.client0.connect() - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Group name should be at least 1 characters.'): await self.client0.query('create group `` for /c.*/') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Group name should be at least 1 characters.'): await self.client0.query('create group `` for /c.*/') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Group name should be at most [0-9]+ characters.'): await self.client0.query( @@ -56,7 +56,7 @@ class TestGroup(TestBase): await self.client0.query('create group `a` for /a.*/'), {'success_msg': "Successfully created group 'a'."}) - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Group \'a\' already exists.'): await self.client0.query('create group `a` for /a.*/') @@ -94,7 +94,7 @@ class TestGroup(TestBase): result = await self.client1.query('list groups series') self.assertEqual(result.pop('groups'), [[2], [2], [2]]) - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, "Cannot compile regular expression.*"): result = await self.client1.query('create group `invalid` for /(/') @@ -137,7 +137,7 @@ class TestGroup(TestBase): result = await self.client0.query('list series `a`, `two` & "c2"') self.assertEqual(sorted(result.pop('series')), [['c2']]) - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, "Cannot compile regular expression.*"): await self.client1.query('alter group `a` set expression /(.*/') @@ -150,7 +150,7 @@ class TestGroup(TestBase): # await self.client0.query('drop group `b`') await self.client0.query('drop group `c`') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Group \'c\' does not exist.'): await self.client0.query('drop group `c`') diff --git a/itest/test_list.py b/itest/test_list.py index 154c0fa8..5c6f9f22 100644 --- a/itest/test_list.py +++ b/itest/test_list.py @@ -94,7 +94,7 @@ class TestList(TestBase): await self.client0.query('list series /.*/ ^ /a.*/ ^ /.*/') await self.client0.query('alter database set list_limit 5000') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Limit must be a value between 0 and 5000 ' 'but received: 6000.*'): diff --git a/itest/test_select.py b/itest/test_select.py index 3d9df9dc..d2070b88 100644 --- a/itest/test_select.py +++ b/itest/test_select.py @@ -19,6 +19,7 @@ from testing import ServerError from testing import SiriDB from testing import TestBase from testing import UserAuthError +from testing import parse_args DATA = { @@ -338,82 +339,82 @@ class TestSelect(TestBase): await self.client0.query('select difference() from "one"'), {'one': []}) - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Regular expressions can only be used with.*'): await self.client0.query('select filter(~//) from "log"') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Cannot use a string filter on number type.'): await self.client0.query('select filter(//) from "aggr"') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Cannot use mean\(\) on string type\.'): await self.client0.query('select mean(1w) from "log"') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Group by time must be an integer value larger than zero\.'): await self.client0.query('select mean(0) from "aggr"') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Limit must be an integer value larger than zero\.'): await self.client0.query('select limit(6 - 6, mean) from "aggr"') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Cannot use a string filter on number type\.'): await self.client0.query( 'select * from "aggr" ' 'merge as "t" using filter("0")') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Cannot use difference\(\) on string type\.'): await self.client0.query('select difference() from "log"') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Cannot use derivative\(\) on string type\.'): await self.client0.query('select derivative(6, 3) from "log"') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Cannot use derivative\(\) on string type\.'): await self.client0.query('select derivative() from "log"') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Overflow detected while using sum\(\)\.'): await self.client0.query('select sum(now) from "huge"') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Max depth reached in \'where\' expression!'): await self.client0.query( 'select * from "aggr" where ((((((length > 1))))))') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Cannot compile regular expression.*'): await self.client0.query( 'select * from /(bla/') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Memory allocation error or maximum recursion depth reached.'): await self.client0.query( 'select * from "aggr" where length > {}'.format('(' * 500)) - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Query too long.'): await self.client0.query('select * from "{}"'.format('a' * 65535)) - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Error while merging points. Make sure the destination ' 'series name is valid.'): @@ -476,7 +477,7 @@ class TestSelect(TestBase): self.assertIn('aggr-maximum', result) await self.client0.query('alter database set select_points_limit 10') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Query has reached the maximum number of selected points.*'): await self.client0.query( @@ -490,9 +491,5 @@ class TestSelect(TestBase): if __name__ == '__main__': - SiriDB.LOG_LEVEL = 'CRITICAL' - Server.HOLD_TERM = True - Server.MEM_CHECK = True - Server.TERMINAL = 'XTERM' - Server.BUILDTYPE = 'Debug' + parse_args() run_test(TestSelect()) diff --git a/itest/test_series.py b/itest/test_series.py index 1bf42eb1..42c6d750 100644 --- a/itest/test_series.py +++ b/itest/test_series.py @@ -33,7 +33,7 @@ data = { 'integer': [ [1538660000, 1], [1538660010, 35.6], - [1538660020, "-50,6%"], + [1538660020, "-50%"], [1538660030, ""], [1538660035, "garbage"], [1538660040, "18446744073709551616"], @@ -42,7 +42,7 @@ data = { 'double': [ [1538660000, 1.0], [1538660010, -35], - [1538660010, "-50,6%"], + [1538660010, "-50%"], [1538660030, ""], [1538660035, "garbage"], ] @@ -67,9 +67,9 @@ expected = { 'double': [ [1538660000, 1.0], [1538660010, -35.0], - [1538660010, -50.6], + [1538660010, -50.0], [1538660030, 0.0], - [1538660035, 0], + [1538660035, 0.0], ] } diff --git a/itest/test_server.py b/itest/test_server.py index 713b0d69..62cac4ee 100644 --- a/itest/test_server.py +++ b/itest/test_server.py @@ -75,7 +75,7 @@ class TestServer(TestBase): result = await self.client1.query('list servers log_level') self.assertEqual(result.pop('servers'), [['debug'], ['debug']]) - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, "Query error at position 42. Expecting " "debug, info, warning, error or critical"): @@ -103,13 +103,13 @@ class TestServer(TestBase): await self.db.add_replica(self.server2, 1) await self.assertIsRunning(self.db, self.client0, timeout=10) - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, "Cannot remove server 'localhost:9010' " "because this is the only server for pool 0"): await self.client1.query('drop server "localhost:9010"') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, "Cannot remove server 'localhost:9012' " "because the server is still online.*"): diff --git a/itest/test_user.py b/itest/test_user.py index 853de74f..505380ba 100644 --- a/itest/test_user.py +++ b/itest/test_user.py @@ -32,17 +32,17 @@ class TestUser(TestBase): with self.assertRaises(QueryError): await self.client0.query('create user "sasientje" ') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'User name should be at least 2 characters.'): await self.client0.query('create user "s" set password "123456" ') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'User name contains illegal characters.*'): await self.client0.query('create user " " set password "123456" ') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, 'Password should be at least 4 characters.'): await self.client0.query('create user "aa" set password "123" ') @@ -97,7 +97,7 @@ class TestUser(TestBase): result = await self.server0.stop() self.assertTrue(result) - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, "Password should be at least 4 characters."): result = await self.client1.query( @@ -118,7 +118,7 @@ class TestUser(TestBase): result = await self.client0.query("show who_am_i ") self.assertEqual(result['data'][0]['value'], 'sasientje') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( UserAuthError, "Access denied. User 'sasientje' has no 'insert' privileges."): result = await self.client0.insert({'no access test': [[1, 1.0]]}) @@ -137,35 +137,35 @@ class TestUser(TestBase): result = await self.client0.query('count users where name == "pee"') self.assertEqual(result.pop('users'), 1, msg='Expecting 1 user (pee)') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( UserAuthError, "Access denied. User 'sasientje' has no 'grant' privileges."): result = await self.client0.query('grant full to user "pee" ') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, "User name should be at least 2 characters."): result = await self.client1.query('alter user "pee" set name "p" ') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, "^User name contains illegal characters.*"): result = await self.client1.query( 'alter user "pee" set name " p " ') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, "User 'iris' already exists."): result = await self.client1.query( 'alter user "pee" set name "iris" ') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, "User 'iris' already exists."): result = await self.client1.query( 'alter user "pee" set name "iris" ') - with self.assertRaisesRegexp( + with self.assertRaisesRegex( QueryError, "Cannot find user: 'Pee'"): result = await self.client1.query( diff --git a/itest/testing/__init__.py b/itest/testing/__init__.py index fe23f73f..282c8ff6 100644 --- a/itest/testing/__init__.py +++ b/itest/testing/__init__.py @@ -18,26 +18,74 @@ from .testbase import default_test_setup from .testbase import TestBase from .series import Series from .pipe_client import PipeClient as SiriDBAsyncUnixConnection +from .args import parse_args + +SPINNER1 = \ + ('▁', '▂', '▃', '▄', '▅', '▆', '▇', '█', '▇', '▆', '▅', '▄', '▃', '▁') +SPINNER2 = \ + ('⠁', '⠂', '⠄', '⡀', '⢀', '⠠', '⠐', '⠈') +SPINNER3 = \ + ('◐', '◓', '◑', '◒') + + +class Spinner(): + + def __init__(self, charset=SPINNER3): + self._idx = 0 + self._charset = charset + self._len = len(charset) + + @property + def next(self): + char = self._charset[self._idx] + self._idx += 1 + self._idx %= self._len + return char + + +class Task(): + def __init__(self, title): + self.running = True + self.task = asyncio.ensure_future(self.process()) + self.success = False + self.title = title + self.start = time.time() + + def stop(self, success): + self.running = False + self.success = success + self.duration = time.time() - self.start + + async def process(self): + spinner = Spinner() + while self.running: + sys.stdout.write(f'{self.title:.<76}{spinner.next}\r') + sys.stdout.flush() + await asyncio.sleep(0.2) + + if self.success: + print(f'{self.title:.<76}OK ({self.duration:.2f} seconds)') + else: + print(f'{self.title:.<76}FAILED ({self.duration:.2f} seconds)') async def _run_test(test, loglevel): logger = logging.getLogger() logger.setLevel(loglevel) - - start = time.time() - print('{:.<76}'.format(test.title), end='') - sys.stdout.flush() + task = Task(test.title) try: await test.run() except Exception as e: - print('FAILED ({:.2f} seconds)'.format(time.time() - start)) + task.stop(success=False) raise e else: - print('OK ({:.2f} seconds)'.format(time.time() - start)) + task.stop(success=True) logger.setLevel('CRITICAL') + await task.task + def run_test(test, loglevel='CRITICAL'): assert isinstance(test, TestBase) diff --git a/itest/testing/args.py b/itest/testing/args.py new file mode 100644 index 00000000..3a7506a2 --- /dev/null +++ b/itest/testing/args.py @@ -0,0 +1,44 @@ +import argparse +from .server import Server + + +def parse_args(): + parser = argparse.ArgumentParser() + + parser.add_argument( + '-t', '--terminal', + choices=['xterm', 'xfce4-terminal'], + default=None, + help='Start SiriDB servers in a terminal. If no terminal is given ' + 'process put their output in log files.') + + parser.add_argument( + '-m', '--mem-check', + action='store_true', + help='Use `valgrind` for memory errors and leaks.') + + parser.add_argument( + '-k', '--keep', + action='store_true', + help='Only valid when a terminal is used. This will keep the terminal ' + 'open.') + + parser.add_argument( + '-b', '--build', + choices=['Release', 'Debug'], + default='Release', + help='Choose either the Release or Debug build.') + + parser.add_argument( + '-l', '--log-level', + default='critical', + help='set the log level', + choices=['debug', 'info', 'warning', 'error', 'critical']) + + args = parser.parse_args() + + Server.MEM_CHECK = args.mem_check + Server.HOLD_TERM = args.keep + Server.TERMINAL = args.terminal + Server.BUILDTYPE = args.build + Server.LOG_LEVEL = args.log_level.upper() diff --git a/itest/testing/server.py b/itest/testing/server.py index ca21e46c..2b9b4b31 100644 --- a/itest/testing/server.py +++ b/itest/testing/server.py @@ -31,6 +31,7 @@ class Server: def __init__(self, n, + title, optimize_interval=300, heartbeat_interval=30, buffer_sync_interval=500, @@ -38,6 +39,7 @@ class Server: pipe_name=None, **unused): self.n = n + self.test_title = title.lower().replace(' ', '_') self.compression = compression self.enable_pipe_support = int(bool(pipe_name)) self.pipe_name = \ @@ -106,9 +108,8 @@ class Server: async def start(self, sleep=None): prev = self._get_pid_set() - - if self.TERMINAL == 'XFCE4_TERMINAL': - rc = subprocess.Popen( + if self.TERMINAL == 'xfce4-terminal': + self.proc = subprocess.Popen( 'xfce4-terminal -e "{}{} --config {} --log-colorized"' ' --title {} --geometry={}{}' .format(VALGRIND if self.MEM_CHECK else '', @@ -118,8 +119,8 @@ class Server: self.GEOMETRY, ' -H' if self.HOLD_TERM else ''), shell=True) - elif self.TERMINAL == 'XTERM': - rc = subprocess.Popen( + elif self.TERMINAL == 'xterm': + self.proc = subprocess.Popen( 'xterm {}-title {} -geometry {} -e "{}{} --config {}"' .format('-hold ' if self.HOLD_TERM else '', self.name, @@ -129,9 +130,13 @@ class Server: self.cfgfile), shell=True) else: - with open(f'{self.name}-err.log', 'a') as err: - with open(f'testdir/{self.name}-out.log', 'a') as out: - rc = subprocess.Popen( + with open( + f'testdir/{self.test_title}-{self.name}-err.log', + 'a') as err: + with open( + f'testdir/{self.test_title}-{self.name}-out.log', + 'a') as out: + self.proc = subprocess.Popen( '{}{} --config {}' .format(VALGRIND if self.MEM_CHECK else '', SIRIDBC.format(BUILDTYPE=self.BUILDTYPE), @@ -139,7 +144,6 @@ class Server: stderr=err, stdout=out, shell=True) - self.assertEqual(rc, 0) await asyncio.sleep(5) @@ -157,6 +161,9 @@ class Server: await asyncio.sleep(1.0) timeout -= 1 + self.proc.communicate() + assert (self.proc.returncode == 0) + if timeout: self.pid = None return True diff --git a/itest/testing/testbase.py b/itest/testing/testbase.py index dfba3d2b..8008e65f 100644 --- a/itest/testing/testbase.py +++ b/itest/testing/testbase.py @@ -14,7 +14,8 @@ def default_test_setup(nservers=1, **kwargs): async def wrapped(self): self.db = SiriDB(**kwargs) - self.servers = [Server(n, **kwargs) for n in range(nservers)] + self.servers = [ + Server(n, title=self.title, **kwargs) for n in range(nservers)] for n, server in enumerate(self.servers): setattr(self, 'server{}'.format(n), server) setattr(self, 'client{}'.format(n), Client(self.db, server)) @@ -117,7 +118,9 @@ class TestBase(unittest.TestCase): 'Expecting a point to be a list of 2 items' super().assertEqual(a[series][i][0], point[0]) if isinstance(a[series][i][1], str): - super().assertEqual(a[series][i][1], point[1]) + super().assertEqual( + a[series][i][1].replace(',', '.'), + point[1].replace(',', '.')) elif math.isnan(a[series][i][1]): assert math.isnan(point[1]), \ 'Expecting point `{}` to be `nan`, got: `{}`' \